home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Resource for Source: Assembly
/
assembly resource for source.iso
/
assem01
/
pop_cal.asm
< prev
next >
Wrap
Assembly Source File
|
1995-11-01
|
23KB
|
378 lines
title POP-CAL - popup calendar for 1583 - 9999 AD.
comment ~
Use Alt-C, to pop up and terminate calendar generator.
Use left/right cursor keys to change months.
Use Up/Down cursor keys to change years.
~
cseg segment
assume cs:cseg
;-----------------------------------------------------------------------------
ppfdata struc ;Variable data is stored in PPF area.
rom16h dw ?,? ;Holds old Interrupt 16h BIOS vector.
savss dw ? ;Holds caller's stack segment
savsp dw ? ;Holds caller's stack pointer
video_seg dw ? ;Holds base segment of video buffer
mnth db ? ;calendar's month
year dw ? ;calendar's year
weekday db ? ;1st day of month 0=Sun,,,,6=Sat
left dw ? ;Offset to left edge of calendar, row 0
hilite db ? ;Calendar display attribute.
busy db ? ;Busy flag, for our stack
crt_mode db ? ;Holds current crt mode
crt_cols dw ? ;Number of columns on screen
status_port dw ? ;video card status port address
ppfdata ends
org 100h-status_port
v ppfdata <> ;Locate ppfdata and assign prefix "v".
;-----------------------------------------------------------------------------
org 100h
begin: jmp Install ;Initialize calendar; go resident.
db "Copyright 1986 Ziff-Davis Publishing Co.",1Ah
days db 'SunMonTueWedThrFriSat'
months db 'JanFebMarAprMayJunJulAugSepOctNovDec'
numdays db 31,28,31,30,31,30,31,31,30,31,30,31 ;Jan-Dec days/month
bios dw 40h ;Points to BIOS data segment
scan_code dw 2e00h ;(Alt)(C) scan code to start/stop program.
;---------------- Intercept keystroke reading --------------------------------
int16h: sti ;Turn interrupts back on.
cmp ah,0 ;See if was request for an actual key.
je come_back ;If so, we'll check it first.
skip_us:jmp dword ptr v.rom16h ;Otherwise, let go straight to caller.
come_back:
cmp v.busy,0 ;Is calendar routine busy?
jne skip_us ;If so, pass all keys along.
getkey: pushf ;Else, call BIOS keyboard routine,
call dword ptr v.rom16h ;so it will return the key to us.
cmp ax,scan_code ;Is scan code correct?
je now_busy ;If so, go to work.
iret ;Else just pass on key to caller.
now_busy:
mov v.busy,1 ;Set busy flag, to protect our stack.
mov v.savss,ss ;Save caller's stack segment.
mov v.savsp,sp ;save caller's stack pointer.
mov sp,cs
cli ;Avoid interrupt right now.
mov ss,sp ;Reset stack to our code segment.
mov sp,offset v.rom16h ;Start our stack below ROM16 addr.
sti
push ax
push bx
push cx
push dx
push bp
push si ;Save all user's registers.
push di
push ds
push es
call begin_calendars ;Go do the calendars.
pop es ;When finished,...
pop ds
pop di
pop si
pop bp ;Restore all user's registers
pop dx
pop cx
pop bx
pop ax
cli
mov ss,v.savss ;restore old stack
mov sp,v.savsp
sti
mov ah,0 ;Reset AH to BIOS key read function.
mov v.busy,0 ;Allow reuse of calendar stack.
jmp getkey ;Go get new key from BIOS.
;--------------- Read keys input. Process calendars. ----------------------
begin_calendars:
push cs
push cs
pop ds ;Set DS and ES to our code segment.
pop es
cld ;Set for standard "up" direction.
assume ds:cseg ;Let Assembler use DS addressing, now.
call wndo ;Save Screen data. Put old calendar on it.
jmp showcal ;Go fill in calendar, in case none made yet.
nxtcal: mov v.mnth,dh ;Store new month value.
mov v.year,cx ;Store new year.
showcal:call fill ;Format and display new calendar.
input: mov ah,0 ;Wait for key stroke.
int 16h
cmp ax,scan_code ;Another (Alt)(C)?
jne getdate ;If not, initialize CX (year) and DH (month).
exit: call wndo ;Restore original screen data and
ret ; go restore caller's stack... all done.
getdate:mov dh,v.mnth ;Load calendar month into DH.
mov cx,v.year ;Load calendar year into CX.
trylft: cmp ah,75 ;Was key a left arrow?
jne tryrgt ;If not, go see if was a right arrow.
dec dh ; If a left arrow, reduce month by 1.
jns nxtcal ; When pass Jan, switch
mov dh,11 ; to Dec of prior year.
dec cx ; Reduce year by one.
jmp chkloyr ; Go check that its still in range.
tryrgt: cmp ah,77 ;Right arrow? (increase month)
jne tryup ;If not, see if an Up arrow.
inc dh ; Was right arrow, so increase month.
cmp dh,11 ; See if past December.
jng nxtcal ; If not, go make the calendar.
mov dh,0 ; If was, reset month to January
inc cx ; and move up to next year.
jmp chkhiyr ; Then insure year is still in range.
tryup: cmp ah,72 ;Up arrow? (increase year)
jne trydwn ;If not, see if was a down key.
inc cx ; If Up, increase year by 1.
chkhiyr:cmp cx,10000 ; Year can only be 4 digits.
jge input ; Ignore key, if date goes out of range.
jmp nxtcal ; Otherwise go make new calendar.
trydwn: cmp ah,80 ;Down arrow? (decrease year)
jne input ;If not, ignore key and go get new one.
dec cx ; Down key reduces year by 1.
chkloyr:cmp cx,1582 ;Calendar calculations only good for 11/1582
jle input ; on, so ignore key, if year now < 1583.
jmp nxtcal ; Otherwise, go make new calendar.
;------------------ Fill in the new calendar ------------------------------
fill: call firstday ;Calculate weekday of 1st of month.
mov es,v.video_seg ;Point ES to write to screen.
mov al,v.mnth ;Put current month into AL.
cbw ;Zeros out AH.
mov cx,3 ;Zero out CH. Put 3 in CL.
mul cl ;AX = # bytes to current month's name
mov si,offset months; from start of "months".
add si,ax ;SI=offset to month's name
mov di,v.crt_cols ;Put screen width (# columns) in DI.
add di,v.left ;Move to left edge, second row.
add di,22 ;Move on to center month in the row.
movname:lodsb ;Load character from month's name.
call writebyte ;Output char of month's name to screen.
loop movname ;Do three times, since cx=3.
add di,2 ;Skip a space, after the month's name.
mov ax,v.year ;Load year into AX.
mov bl,100 ;Will use to split year into centuries
div bl ; portion and remaining years in century.
mov bh,1 ;Set flag, so will retain leading zero.
call unpack ;Display the centuries portion of year.
mov al,ah ;Put remainder in al.
call unpack ;Display remaining 2 digits in year.
setdi: mov ax,v.crt_cols ;Load current screen width.
mov bl,4 ;Mult by # rows to skip.
mul bl ;Calculate offset to 4th row of calendar.
add ax,4 ;Skip in 2 columns on that row.
mov di,v.left ;Set to offset of top left corner of calendar.
add di,ax ;Move to location of 1st date on screen.
mov dl,v.weekday ;Retrieve week day of 1st of cal's month.
mov cl,dl ;Store as count of days to blank in 1st week.
mov dh,6 ;DH = # weeks left to fill in.
jcxz start_current_month ;If Sunday, CX = 0 and no blanking needed.
blnkrw1:call blank2 ;Blank out 1st part of 1st week.
loop blnkrw1
start_current_month:
mov cl,7 ;Number of days to display in 1st week
sub cl,dl ; = 7 - weekday of 1st day.
mov bl,v.mnth ;Set index for days-per-month array.
xor bh,bh ;Set flag to suppress leading zeros.
mov dl,numdays[bx] ;Get # days in current month.
mov al,1 ;Set AL to 1st day of month.
showdays:
call unpack ;Format and display the date value.
inc al ;Increment to next day.
add di,4 ;Skip to next day position.
dec dl ;Reduce # days left in month.
jz blnkrst ;If last day, blank rest of the 6 weeks.
loop showdays ;Otherwise continue displaying dates in week.
call nxtweek ;When reach end of a week, space to next one.
jmp showdays ; and continue on new row of calendar.
blnkrw4:call blank2 ;Blank out remaining days in week.
blnkrst:loop blnkrw4 ;Blank to end of current week.
call nxtweek ;Relocate DI to 1st day of next week.
jnz blnkrw4 ;If not past last week, go blank week out.
push cs ;When all done, restore ES to CS.
pop es
ret ;Return to keyboard reading routine.
;---------------------- Move DI to 1st date in next week ---------------------
nxtweek:add di,v.left ;Move to left edge of next row of calendar.
add di,4 ;Move in an additional 2 columns.
mov cl,7 ;Restart week count.
dec dh ;Reduce weeks left in calendar.
ret
;---------------------- Compute weekday of 1st day of month ------------------
firstday:
mov si,v.year ;Hold year in SI, of quick access.
mov numdays[1],28 ;Assume this isn't a leap year.
test si,3 ;Now see if year divisible by 4.
jnz getday ;If not, was actually not a leap year.
feb29: mov numdays[1],29 ;Else was leap year with 29 days.
getday: mov bx,6 ;BX=Saturday,(weekday of 1/1/1583)
mov ax,si ;Calendar year to AX.
sub ax,1583 ;Calc. # years since our base year.
add bx,ax ;Calendar advances 1 weekday per
; year unless leap years intervene.
add ax,2 ;Adj diff, so even #, if the calendar
mov cx,4 ; year was year after a leap year.
cwd ;Zero out DX.
div cx ;Calculate # leap years before this.
add bx,ax ;Add 1 day to week, for each leap yr.
cmp si,1700 ;Things work normally 'til 1700, which
jl cnvrt ;not a leap year, per Gregorian cal.
mov ax,si ;Year back to AX, again.
sub ax,1600 ;Get # years since 1600.
mov cl,100
cwd ;Convert to number of centuries.
div cx
cmp dx,0 ;If remainder=0 this is centennial yr.
jne deduct
test ax,3 ;If century is evenly divisible by
jz decax ; 400, it is also still a leap year.
mov numdays[1],28 ;Other centenial years have 28 days.
decax: dec ax ;If centenial yr, sub 1 from centuries
deduct: sub bx,ax ;Subtract 1 weekday per century.
cwd
mov cl,4
div cx ;Calculate centuries mod 400.
add bx,ax ;Add back 1 day per 400 years.
cnvrt: mov si,offset numdays ;Add in the days per month, this yr.
xor ah,ah ;First clear hi byte of accumulator
mov cl,v.mnth ;Load cx with month counter
jcxz final ;If January, are ready for final calc.
addmnth:lodsb ;Get low byte into accumulator.
add bx,ax ;Add to days count.
loop addmnth ;Continue until prior months added in.
final: mov ax,bx ;AX=# days advanced since 1/1/1583.
cwd ;Clear out DX.
mov cl,7 ;Find # full weeks since 1583.
div cx ;DL=weekday of 1st of calendar's month
mov v.weekday,dl ;Save results of this work.
ret
;-------------- Convert binary # in AL to ASCII and output to ES:DI ----------
unpack: push ax ;(AL should binary # from 0-99.)
push bx
aam ;Divide al by 10.
add ax,3030h ;Convert digits to ASCII.
cmp ah,30h ;See if 10's digit was a zero.
jne putnum ;If not, go output it to screen.
cmp bh,1 ;If BH=1, leading zero is ok, so
je putnum ; go write it. Otherwise, replace
mov ah,20h ; leading zero with a blank.
putnum: mov bl,al ;Need to write high order digit first,
mov al,ah ; so save low digit and move hi into AL.
call writebyte ;Output 10's digit to screen.
mov al,bl ;Put 1's digit back in AL.
call writebyte ;Write 1's digit.
pop bx ;Restore BX.
pop ax ;Retrieve old binary value.
ret ;All done.
;------------------- Blank out 2 digit calendar date field. Update DI. ------
blank2: mov al,20h ;Put a blank in AL
call writebyte ;Write a blank over 1st digit.
call writebyte ;Write a blank over 2nd digit.
add di,4 ;Skip 2 spaces, to next date.
ret
;------------------- Write char AL on screen at ES:DI. Update DI. ------------
writebyte:
push dx ;Save DX.
mov dx,v.status_port ;Reset to video status port address.
mov ah,al ;Move character to AH, for now.
on1: in al,dx ;Wait until not doing a
test al,1 ; horizontal scan retrace.
jnz on1
cli
on2: in al,dx ;Now wait until next horizontal
test al,1 ; retrace begins, so KNOW will have
jz on2 ; entire retrace time period to work.
mov al,ah ;Retrieve character from AH.
stosb ;Store character on screen.
sti
inc di ;Skip over attribute byte on screen.
pop dx ;Restore old DX value.
ret
;---------------------- Switch memory field with screen data -----------------
wndo: call getprms ;Get current video parms
mov dx,v.status_port ;Point DX to Status Port.
sub dx,2 ;Back up to control port.
mov es,bios ;Point to ROM BIOS data segment
mov bh,es:[65h] ;Get current setting of video card.
mov al,bh ;Move to AL, for output, after reset.
and al,0f7h ;Turn off video enable bit only.
out dx,al ;Turn off screen.
mov si,offset data_strt ;Point to calendar data in memory.
mov di,v.left ;Set DI=upper left corner of calendar.
mov es,v.video_seg ;Set ES to video buffer segment.
mov bl,11 ;11 rows in the calendar.
nxtlin: mov cx,30 ;There are 30 words per calendar row.
getchr: mov ax,es:[di] ;Get next word from screen buffer.
movsw ;Move data word to screen.
mov [si-2],ax ;Store screen character in our data.
loop getchr ;Continue across row.
add di,v.left ;Move to start of next calendar row.
dec bl ;Reduce rows-to-go count.
jnz nxtlin ;Do next row, if more to go.
mov al,bh ;Restore video setting to original
out dx,al ; and turn on screen again.
push cs
pop es ;Restore ES to our code segment.
ret
;------------------- Get video display parameters ----------------------------
getprms:push es
mov es,bios ;Point ES to BIOS data segment.
mov ax,es:[4ah] ;Put current column width.
shl ax,1 ;Multiply by 2, for attribute bytes.
mov v.crt_cols,ax ;Store in our data, for easy access.
sub al,30*2 ;Back off from right side, by width of
mov v.left,ax ;calendar. Equals offset to left edge.
mov ax,es:[63h] ;Get base address of video card.
add ax,6 ;Calculate status port address and
mov v.status_port,ax ; save for checking horiz scans.
mov v.hilite,70h ;Black on white background.
mov bl,es:[49h] ;Get video mode into BL.
cmp bl,7 ;In Monochrome video mode?
mov ax,0B000h ;Monochrome buffer seg at 0B000h.
je setseg ;If monochrome, go store video seg.
mov ax,0B800h ;If graphics card, video seg=0B800h.
test bl,1 ;1,3 are color text on graphics card.
jz setseg
mov v.hilite,1fh ;Use bright white on blue, if color.
setseg: mov v.video_seg,ax ;Store video_segment value
pop es ;Restore Es.
ret ;Done.
;------------------ Create calendar form and become resident. ---------------
Install:call getprms ;Call, to get correct color attribute.
mov di,offset data_strt ;Point to end of our code segment.
mov ah,v.hilite ;Set attribute byte in AH.
mov al,20h ;Will store blanks in calendar area.
mov cx,11*30 ;11 rows of 30 columns each.
rep stosw ;Blank out calendar work area
push di ;Save end of calendar offset.
mov si,offset days ;Now store day's names in calendar.
mov bl,7 ;Set names-to-go counter to 7.
mov di,offset data_strt ;Point to start of calendar
add di,3*30*2+4 ;Skip in 3 rows + 2 columns.
movday: mov cx,3 ;There are 3 chars per name.
movchr: movsb ;Transfer character into calendar.
inc di ;Skip over attribute byte.
loop movchr ;Continue for three characters.
inc di
inc di ;Add 2 to DI, to skip space.
dec bl ;Reduce names-to-go count.
jnz movday ;Continue until all stored.
mov ah,2ah ;Get current date from DOS.
int 21h
dec dh ;Convert months,so Jan=0, etc.
mov v.mnth,dh ;Save month and year values.
mov v.year,cx
go_resident:
xor ax,ax
mov es,ax ;Set ES to 0.
mov ax,es:[16h*4]
mov v.rom16h,ax ;Store rom interrupt 16h address.
mov ax,es:[16h*4+2]
mov v.rom16h+2,ax
mov ax,offset int16h
cli
mov es:[16h*4],ax ;Point Interrupt 16 to our code.
mov es:[16h*4+2],cs
sti
pop dx ;Retrieve address of end of data.
int 27h ;Now become resident.
;----------------- Calendar data area --------------------------------
data_strt equ this word
cseg ends
end begin